package com.candy.apk.parser;

import brut.androlib.AndrolibException;
import brut.androlib.ApkDecoder;
import com.candy.apk.decode.DecompileResult;
import com.candy.apk.domain.ApkInfo;
import com.candy.apk.domain.IApkInfo;
import com.candy.apk.exception.ApkParseException;
import com.candy.apk.resolver.DefaultManifestParseHandler;
import com.candy.apk.util.Constant;
import com.candy.apk.util.IOUtils;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;

/**
 * Created by yantingjun on 2015/3/22.
 */
public class ApkAssistant extends IAssistant {

    @Override
    public IApkInfo getApkInfo(String apkFilePath) throws ApkParseException {
        checkConfiguration();
        DecompileResult decodeResult = decompile(apkFilePath, tempPath);
        if(decodeResult==null){
            throw new ApkParseException("Decompile apk failed.Reason:unknowm");
        }
        if(!decodeResult.isSuccess()){
            throw new ApkParseException(decodeResult.getMsg());
        }
        return resolve(decodeResult.getManifestFilePath());
    }

    private void checkConfiguration() throws ApkParseException {
        if(getManifestParseHandler()== null){
            throw new ApkParseException("Manifest parse handler was not specified yet!");
        }
        if(getTempPath()==null || getTempPath().length()<=0){
            throw new ApkParseException("Manifest parse handler was not specified yet!");
        }
    }

    private IApkInfo resolve(String manifestFilePath) throws ApkParseException {
        File manifestFile = new File(manifestFilePath);
        if(!manifestFile.exists()){
            throw new ApkParseException(Constant.ManifestFile+" was not found!");
        }
        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
        parserFactory.setValidating(false);
        parserFactory.setNamespaceAware(false);

        SAXParser parser = null;
        FileInputStream fis = null;
        try {
            fis =  new FileInputStream(manifestFile);
            parser = parserFactory.newSAXParser();

            parser.parse(fis, getManifestParseHandler());
            return getManifestParseHandler().getApkInfo();
        } catch (Exception e) {
            e.printStackTrace();
        }  finally {
            IOUtils.close(fis);
        }
        return null;
    }

    public DecompileResult decompile(String apkFilePath, String tempPath){
        DecompileResult decodeResult = new DecompileResult();

        File apkFile = new File(apkFilePath);
        if(!apkFile.exists()){
            decodeResult.setMsg("File not found:"+apkFilePath);
            return decodeResult;
        }
        File tempDir = new File(tempPath);
        if(!tempDir.exists()){
            decodeResult.setMsg("Directory not found:"+tempDir.getAbsolutePath());
            return decodeResult;
        }
        ApkDecoder apkDecoder = new ApkDecoder();
        apkDecoder.setApkFile(apkFile);
        try {
            File outDir = getFile(apkFile, tempDir);

            apkDecoder.setOutDir(outDir);
            apkDecoder.decode();
            File manifestFile = findManifestFile(outDir);
            if(manifestFile==null||!manifestFile.exists()){
                decodeResult.setMsg(Constant.ManifestFile+" was not found!");
                return decodeResult;
            }
            decodeResult.setSuccess(true);
            decodeResult.setMsg("Decompile success!");
            decodeResult.setManifestFilePath(manifestFile.getAbsolutePath());
            return decodeResult;
        } catch (AndrolibException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private File getFile(File apkFile, File tempDir) {
        File file = new File(tempDir+File.separator+getFileName(apkFile));
        int num = 1;
        while (file.exists()){
            file = new File(tempDir+File.separator+getFileName(apkFile)+"("+num+")");
        }
        return file;
    }

    private File findManifestFile(File outDir) {
        if(outDir==null||!outDir.exists()){
            return null;
        }
        File[] files = outDir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return Constant.ManifestFile.equals(name);
            }
        });
        if(files==null||files.length<=0){
            return null;
        }
        return files[0];
    }

    private String getFileName(File apkFile) {
        String fileName = apkFile.getName();
        if(fileName.contains(".")){
            return fileName.substring(0,fileName.lastIndexOf("."));
        }
        return fileName;
    }

    public static void main(String[] args) {
        IAssistant assistant = new ApkAssistant();
        assistant.setManifestParseHandler(new DefaultManifestParseHandler());
        assistant.setTempPath("F:\\Java\\test");
        try {
            ApkInfo apkInfo = (ApkInfo)assistant.getApkInfo("F:\\Java\\test\\mini.1.21.cmccmm.apk");
            System.out.println(apkInfo.getPackageName());
            System.out.println(apkInfo.getVersionName());
            System.out.println(apkInfo.getVersionCode());
            System.out.println(apkInfo.getApplicationName());
            System.out.println(apkInfo.getApplicationIcon());
        } catch (ApkParseException e) {
            e.printStackTrace();
        }
    }
}
